from logging import getLogger

from zope.interface import implements

from meercat.interfaces import IOrderedRegistry



log = getLogger('meercat.base.registry')

class OrderedRegistry(object):
    implements(IOrderedRegistry)
    
    def __init__(self, mustProvide=None):
        self.mapping = {}
        self.ordered = []
        self.mustProvide = mustProvide
    
    def register(self, name, component=None, remove=False,
                 first=False, last=False, before=None, after=None):
        """ Register a new component (or remove it) """
        if remove:
            log.debug('%s.register() removing %s' % (str(self), str(name),))
            return self.__delitem__(name)
        
        if component is None and not name in self.mapping:
            raise ValueError('A component is required if not removing or moving')
        
        if component is not None and component in self.ordered and self.mapping.get(name, None) != component:
            raise ValueError('This component is already registered with another name')
        
        if component is not None and self.mustProvide and not self.mustProvide.providedBy(component):
            raise TypeError('Component must provide %s' % (str(self.mustProvide)))
        
        if before == '*':
            first = True
            before = None
        
        if after == '*' or not after and not name in self.mapping:
            last = True
            after = None
        
        if before and not before in self.mapping:
            raise ValueError('Invalid before value, %s is not registered' % (str(before),))
        
        if after and not after in self.mapping:
            raise ValueError('Invalid after value, %s is not registered' % (str(after),))
        
        if before == name:
            raise ValueError('%s cannot be inserted before itself' % (str(name),))
        
        if after == name:
            raise ValueError('%s cannot be inserted after itself' % (str(name),))
        
        if (before or after or first or last) and name in self.mapping:
            log.debug('%s.register() removing %s for re-insertion' % (str(self),
                                                                      str(name),))
            if not component:
                component = self.mapping[name]
            
            self.__delitem__(name)
        elif name in self.mapping and component:
            log.debug('%s.register() replacing an existing component: %s' % (str(self),
                                                                             str(name)))
            return self.__setitem__(name, component)
        
        self.mapping[name] = component
        
        if first:
            log.debug('%s.register() inserting component first: %s' % (str(self),
                                                                       str(name),))
            self.ordered.insert(0,component)
        
        elif before:
            log.debug('%s.register() inserting %s before %s' % (str(self),
                                                                str(name),
                                                                str(before),))
            self.ordered.insert(self.ordered.index(self.mapping[before]), component)
        elif after:
            log.debug('%s.register() inserting %s after %s' % (str(self),
                                                                str(name),
                                                                str(after),))
            self.ordered.insert(self.ordered.index(self.mapping[after])+1, component)  
        elif last:
            log.debug('%s.register() inserting component last: %s' % (str(self),
                                                                      str(name),))
            self.ordered.append(component)
    
    def __iter__(self):
        return self.ordered.__iter__()
    
    def __len__(self):
        """ Return the number of registered components """
        return len(self.ordered)
        
    def __contains__(self, name):
        """ Test for containment """
        return name in self.mapping
    
    def __getitem__(self, name):
        """ Return a named component """
        return self.mapping.__getitem__(name)
    
    def __setitem__(self, name, component):
        """ Replace a named component in it's existing location """
        if not name in self.mapping:
            return self.register(name, component)
        
        if self.mustProvide and not self.mustProvide.providedBy(component):
            raise TypeError('Component must provide %s' % (str(self.mustProvide)))
        
        self.ordered[self.ordered.index(self.mapping[name])] = component
        self.mapping[name] = component
    
    def __delitem__(self, name):
        """ Remove a named component """
        if not name in self.mapping:
            raise KeyError('%s is not a registered component')
        
        self.ordered.remove(self.mapping[name])
        del self.mapping[name]
    
        